package com.zyw.configuration;

import com.zyw.module.console.admin.auth.FormAuthService;
import com.zyw.module.console.admin.auth.MiniAppsAuthcService;
import com.zyw.module.console.admin.auth.QywxAuthService;
import com.zyw.module.console.admin.auth.SmsAuthService;
import com.zyw.security.shiro.web.authc.pam.MultiModularRealmAuthenticator;
import com.zyw.security.shiro.web.authc.tk.factory.ShiroAuthcTokenFactory;
import com.zyw.security.shiro.web.filter.UserAccessFilter;
import com.zyw.security.shiro.web.filter.UserLoginAuthenticationFilter;
import com.zyw.security.shiro.web.realm.FormAuthRealm;
import com.zyw.security.shiro.web.realm.SmsAuthRealm;
import com.zyw.security.shiro.web.realm.WXMiniAppsAuthRealm;
import com.zyw.security.shiro.web.realm.WeixinQYAuthRealm;
import com.zyw.security.shiro.web.session.SessionConfig;
import com.zyw.security.shiro.web.session.ShiroJ2CacheSessionDao;
import com.zyw.security.shiro.web.session.WebSessionManager;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSubjectFactory;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.web.filter.DelegatingFilterProxy;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zhangyw
 * @date 2017/12/18 14:40
 */
@ConfigurationProperties(prefix = "spring.shiro")
public class ShiroWebConfiguration {

    private String loginAuthUrl = "/login";
    private String loginPageUrl = "/login.html";
    private String successPageUrl = "/index.html";

    // web filter mapping urls
    private String[] filterUrls = {"/*"};

    public String getLoginAuthUrl() {
        return loginAuthUrl;
    }

    public void setLoginAuthUrl(String loginAuthUrl) {
        this.loginAuthUrl = loginAuthUrl;
    }

    public String getLoginPageUrl() {
        return loginPageUrl;
    }

    public void setLoginPageUrl(String loginPageUrl) {
        this.loginPageUrl = loginPageUrl;
    }

    public String getSuccessPageUrl() {
        return successPageUrl;
    }

    public void setSuccessPageUrl(String successPageUrl) {
        this.successPageUrl = successPageUrl;
    }

    public String[] getFilterUrls() {
        return filterUrls;
    }

    public void setFilterUrls(String[] filterUrls) {
        this.filterUrls = filterUrls;
    }

    @Bean
    public FormAuthRealm formAuthRealm(FormAuthService formAuthService) {
        return new FormAuthRealm(formAuthService);
    }

    @Bean
    public SmsAuthRealm smsAuthRealm(SmsAuthService smsAuthService) {
        return new SmsAuthRealm(smsAuthService);
    }

    @Bean
    public WeixinQYAuthRealm weixinQYAuthRealm(QywxAuthService qywxAuthService) {
        return new WeixinQYAuthRealm(qywxAuthService);
    }

    @Bean
    public WXMiniAppsAuthRealm wxMiniAppsAuthRealm(MiniAppsAuthcService miniAppsAuthcService) {
        return new WXMiniAppsAuthRealm(miniAppsAuthcService);
    }

    /**
     * 配置 securityManager
     *
     * @param realms realms 根据配置的 realm bean 自动装载
     * @return SecurityManager
     */
    @Bean
    public SecurityManager securityManager(List<Realm> realms) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        WebSessionManager sessionManager = new WebSessionManager();
        // 设置 默认 cookie 策略
        SimpleCookie simpleCookie = new SimpleCookie();
        simpleCookie.setName(SessionConfig.auth_tks_name);
        simpleCookie.setMaxAge(1800);
        simpleCookie.setPath("/");
        sessionManager.setSessionIdCookie(simpleCookie);

        // 默认使用 内存 存储 session , 后续替换成 缓存策略
        sessionManager.setSessionDAO(new ShiroJ2CacheSessionDao());

        securityManager.setSessionManager(sessionManager);
        // 设置 subjectDao
        securityManager.setSubjectDAO(new DefaultSubjectDAO());
        // 设置 subject 创建工厂，关闭session 创建
        securityManager.setSubjectFactory(new DefaultWebSubjectFactory());

        securityManager.setAuthenticator(new MultiModularRealmAuthenticator());

        // 配置 Realm
        securityManager.setRealms(realms);

        // 设置 securityUtils manager ，方便全局调用
        SecurityUtils.setSecurityManager(securityManager);
        return securityManager;
    }

    @Bean(value = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) throws Exception {
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
        filterFactoryBean.setSecurityManager(securityManager);
        filterFactoryBean.setFilterChainDefinitionMap(filterChainDefinition().getFilterChainMap());
        filterFactoryBean.setFilters(filters());
        filterFactoryBean.setLoginUrl(getLoginPageUrl());
        filterFactoryBean.setSuccessUrl(getSuccessPageUrl());
        return filterFactoryBean;
    }

    private Map<String, Filter> filters() {
        Map<String, Filter> filters = new LinkedHashMap<>();

        UserLoginAuthenticationFilter authcFilter = new UserLoginAuthenticationFilter();
        authcFilter.setName(authcFilter.getClass().getName());
        authcFilter.setLoginAuthUrl(getLoginAuthUrl());
        authcFilter.setShiroAuthcTokenFactory(new ShiroAuthcTokenFactory());

        filters.put("authc", authcFilter);
        filters.put("user", new UserAccessFilter());
        return filters;
    }

    /**
     * 构造过滤链
     * 注意过滤链的配置先后顺序，正则匹配路径最大者，滞后，否则最小的无法正常匹配
     *
     * @return filter
     */
    private ShiroFilterChainDefinition filterChainDefinition() {
        DefaultShiroFilterChainDefinition filterChainDefinition = new DefaultShiroFilterChainDefinition();
        filterChainDefinition.addPathDefinition("/static/**", "anon");
        //获取短信验证码加入白名单
        filterChainDefinition.addPathDefinition("/auth/sms", "anon");
        //获取企业微信二维码加入白名单
        filterChainDefinition.addPathDefinition("/auth/qrCode", "anon");
        filterChainDefinition.addPathDefinition(getLoginAuthUrl(), "authc");
//        filterChainDefinition.addPathDefinition(PropertyHolder.getString("shiro.logoutAuthUrl", "/auth/logout"), "user");
//        filterChainDefinition.addPathDefinition(PropertyHolder.getString("path.console.prefix") + "/**", "user");
//        filterChainDefinition.addPathDefinition(PropertyHolder.getString("path.rest.prefix") + "/**", "user");
        filterChainDefinition.addPathDefinition("/**", "user");
        return filterChainDefinition;
    }

    /**
     * 代理 和 配置 webxml中的 filter
     *
     * @return FilterRegistrationBean
     */
    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
        //  该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
        filterRegistration.addInitParameter("targetFilterLifecycle", "true");
        filterRegistration.setEnabled(true);
        filterRegistration.setOrder(Integer.MIN_VALUE);
        filterRegistration.addUrlPatterns(getFilterUrls());// 可以自己灵活的定义很多，避免一些根本不需要被Shiro处理的请求被包含进来
        return filterRegistration;
    }

}